package main

import (
	"bytes"
	"crypto/aes"
	"crypto/cipher"
	"crypto/hmac"
	"crypto/md5"
	"crypto/sha256"
	"encoding/base64"
	"encoding/hex"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"strings"
	"time"
)

const (
	// 控制台->设置中心->用户设置->Secret字段
	APP_SECRET = ""
	// 控制台->设置中心->用户设置->Key字段
	APP_KEY = ""
	// API ID 在客户端API页面
	API_ID = 9999
	IV     = "1234567891234567"
	// 该域名为测试环境域名，上线后请参照文档改为生产域名
	HOST_BETA = "https://beta-callback.hgj.com/ingress-ng/openapi-rest/"
)

type Result struct {
	Code      int    `json:"code"`
	Data      string `json:"data"`
	Message   string `json:"message"`
	Timestamp string `json:"timestamp"`
}

func main() {
	sendManifes()
}
func sendManifes() {
	reqUrl := HOST_BETA + "api/v1/" //地址见客户端api详情
	reqBody := ""
	timeUnix := time.Now().Unix()
	sign := GeneratorSign(APP_SECRET, "HMACSHA256")
	param := map[string]interface{}{
		"appKey":    APP_KEY,
		"apiId":     API_ID,
		"timestamp": timeUnix,
		"iv":        IV,
		"sign":      sign,
		"body":      PswEncrypt(reqBody, APP_SECRET, IV),
	}
	client := &http.Client{}
	bytesData, _ := json.Marshal(param)
	req, _ := http.NewRequest("POST", reqUrl, bytes.NewReader(bytesData))
	res, _ := client.Do(req)
	body, _ := ioutil.ReadAll(res.Body)
	// 将body数据转换成result结构体
	var result Result
	json.Unmarshal(body, &result)
	if result.Code == 200 {
		fmt.Println("发送成功")
		fmt.Println("返回结果：", PswDecrypt(result.Data, APP_SECRET, IV))
	} else {
		fmt.Println("发送失败")
		fmt.Println(string(body))
	}
}

// package aes128cbc  以下是ase加密解密内容 实际项目中可以放到指定包中 调用时候 包名.方法名
//加密 参数1：加密内容  参数2：加密key  参数3：加密向量
func PswEncrypt(data, APP_SECRET, IV string) string {
	key := []byte(APP_SECRET)
	iv := []byte(IV)

	result, err := Aes128Encrypt([]byte(data), key, iv)
	if err != nil {
		panic(err)
	}
	return base64.StdEncoding.EncodeToString(result)
}

//解密 加密内容需要base64解码
func PswDecrypt(data, APP_SECRET, IV string) string {
	key := []byte(APP_SECRET)
	iv := []byte(IV)

	var result []byte
	var err error

	result, err = base64.StdEncoding.DecodeString(data)
	if err != nil {
		panic(err)
	}
	origData, err := Aes128Decrypt(result, key, iv)
	if err != nil {
		panic(err)
	}
	return string(origData)

}

// 加密函数 参数1：加密内容  参数2：加密key  参数3：加密向量
func Aes128Encrypt(origData, key []byte, IV []byte) ([]byte, error) {
	if key == nil || len(key) != 16 {
		return nil, nil
	}
	if IV != nil && len(IV) != 16 {
		return nil, nil
	}

	block, err := aes.NewCipher(key)
	if err != nil {
		return nil, err
	}
	blockSize := block.BlockSize()
	if len(origData)%blockSize != 0 {
		origData = PKCS5Padding(origData, blockSize)
	}
	blockMode := cipher.NewCBCEncrypter(block, IV)
	crypted := make([]byte, len(origData))
	blockMode.CryptBlocks(crypted, origData)
	return crypted, nil
}

// 解密函数 参数1：加密内容  参数2：加密key  参数3：加密向量
func Aes128Decrypt(crypted, key []byte, IV []byte) ([]byte, error) {
	if key == nil || len(key) != 16 {
		return nil, nil
	}
	if IV != nil && len(IV) != 16 {
		return nil, nil
	}

	block, err := aes.NewCipher(key)
	if err != nil {
		return nil, err
	}
	blockSize := block.BlockSize()
	blockMode := cipher.NewCBCDecrypter(block, IV)
	origData := make([]byte, len(crypted))
	blockMode.CryptBlocks(origData, crypted)
	if len(origData)%blockSize != 0 {
		origData = PKCS5UnPadding(origData)
	}
	return origData, nil
}

// 填充算法
func PKCS5Padding(ciphertext []byte, blockSize int) []byte {
	padding := blockSize - len(ciphertext)%blockSize
	padtext := bytes.Repeat([]byte{byte(0)}, padding)
	return append(ciphertext, padtext...)
}

func PKCS5UnPadding(origData []byte) []byte {
	length := len(origData)
	// 去掉最后一个字节 unpadding 次
	unpadding := int(origData[length-1])
	return origData[:(length - unpadding)]
}

// package sign 以下为签名算法 包 实际项目可以提出整理一个包 用法 包名.函数名
// md5签名算法
func MD5(v string) string {
	d := []byte(v)
	m := md5.New()
	m.Write(d)
	return strings.ToUpper(hex.EncodeToString(m.Sum(nil)))
}

// hmac签名
func EncodeToHMACSHA256(message string, secret string) string {
	key := []byte(secret)
	h := hmac.New(sha256.New, key)
	h.Write([]byte(message))
	sha := hex.EncodeToString(h.Sum(nil))
	return strings.ToUpper(sha)
}

// 生成签名
func GeneratorSign(APP_SECRET, signType string) string {
	message := "key=" + APP_SECRET
	if signType == "MD5" {
		return MD5(message)
	} else if signType == "HMACSHA256" {
		return EncodeToHMACSHA256(message, APP_SECRET)
	} else {
		panic("Invalid sign_type: " + signType)
	}
}
